#include "MorphologicalOperation.h"


MorphologicalOperation::MorphologicalOperation (SEType structElemType /*= RECT*/,
		int width /*= 3*/, int height /*= 3*/, int centerX /*= -1*/,
		int centerY /*= -1*/) : _structElem (NULL), _tempImage (NULL)
{
	_createNewSE (structElemType, width, height, centerX, centerY);
}

MorphologicalOperation::~MorphologicalOperation ()
{
	if (_structElem)
		cvReleaseStructuringElement (&_structElem);
}

void MorphologicalOperation::_createNewSE (SEType structElemType, int width,
		int height, int centerX /*= -1*/, int centerY /*= -1*/)
{
	if (centerX < 0)
		centerX = width / 2;
	if (centerY < 0)
		centerY = height / 2;

	_structElem = cvCreateStructuringElementEx (width, height,
			centerX, centerY, structElemType);
}

void MorphologicalOperation::changeStructElem (SEType structElemType,
		int width, int height, int centerX /*= -1*/, int centerY /*= -1*/)
{
	if (_structElem)
		cvReleaseStructuringElement (&_structElem);
	_createNewSE (structElemType, width, height, centerX, centerY);
}

void MorphologicalOperation::dilate (const IplImage* inImage,
		IplImage* outImage, int iterations /*= 1*/)
{
	cvDilate (inImage, outImage, _structElem, iterations);
}

void MorphologicalOperation::erosion (const IplImage* inImage,
		IplImage* outImage, int iterations /*= 1*/)
{
	cvErode (inImage, outImage, _structElem, iterations);
}

void MorphologicalOperation::open (const IplImage* inImage, IplImage* outImage,
		int iterations /*= 1*/)
{
	_createTempImage (inImage);
	cvMorphologyEx (inImage, outImage, _tempImage, _structElem, CV_MOP_OPEN, iterations);
}

void MorphologicalOperation::close (const IplImage* inImage, IplImage* outImage,
		int iterations /*= 1*/)
{
	_createTempImage (inImage);
	cvMorphologyEx (inImage, outImage, _tempImage, _structElem, CV_MOP_CLOSE, iterations);
}

void MorphologicalOperation::_createTempImage (const IplImage* inImage)
{
	if (_tempImage && (_tempImage->width != inImage->width ||
		_tempImage->height != inImage->height)) {
			cvReleaseImage (&_tempImage);
			_tempImage = NULL;
	}
	if (!_tempImage)
		_tempImage = cvCreateImage (cvSize (inImage->width, inImage->height), 8, 1);
}

void MorphologicalOperation::thin (const IplImage* inImage, IplImage* outImage)
{
#define IMAGE_LINE(__image__, __line__index__) (uchar*)((__image__)->imageData + (__image__)->widthStep * (__line__index__))
#define SWAP(__image1__, __image2__) { IplImage* __temp_pointer__ = __image1__; __image1__ = __image2__; __image2__ = __temp_pointer__; }
	_createTempImage (inImage);
	cvCopy (inImage, outImage);
	int changed = true;
	int neighbors[9]; // start at right-top, first and last are the same point
	int neighborX[9] = {1, 1, 1, 0, -1, -1, -1, 0, 1};
	int neighborY[9] = {-1, 0, 1, 1, 1, 0, -1, -1, -1};
	IplImage* srcImage = outImage;
	IplImage* dstImage = _tempImage;
	while (changed) {
		changed = false;
		// ** BUG in border of the image **
		for (int v = 0; v < inImage->height; ++v) {
			uchar* srcLine = IMAGE_LINE (srcImage, v);
			uchar* dstLine = IMAGE_LINE (dstImage, v);
			for (int u = 0; u < inImage->width; ++u) {
				dstLine[u] = srcLine[u];
				if (srcLine[u] == 255) {
					int nonzeroNum = 0;
					int zeroOnePatternNum = 0;
					for (int i = 0; i < 9; ++i) {
						neighbors[i] = srcLine[u + neighborX[i] +
								neighborY[i] * inImage->widthStep];
						if (i != 0) {
							if (neighbors[i] != 0) {
								++nonzeroNum;
								if (neighbors[i - 1] == 0)
									++zeroOnePatternNum;
							}
						}
					}
					if (nonzeroNum >= 2 && nonzeroNum <= 6 &&
							zeroOnePatternNum == 1 &&
							(neighbors[1] == 0 || neighbors[3] == 0 ||
							(neighbors[5] == 0 && neighbors[7] == 0))) {
						changed = true;
						dstLine[u] = 0;
					}
				}
			}
		}
		SWAP (srcImage, dstImage);
		for (int v = 0; v < inImage->height; ++v) {
			uchar* srcLine = IMAGE_LINE (srcImage, v);
			uchar* dstLine = IMAGE_LINE (dstImage, v);
			for (int u = 0; u < inImage->width; ++u) {
				dstLine[u] = srcLine[u];
				if (srcLine[u] == 255) {
					int nonzeroNum = 0;
					int zeroOnePatternNum = 0;
					for (int i = 0; i < 9; ++i) {
						neighbors[i] = srcLine[u + neighborX[i] +
							neighborY[i] * inImage->widthStep];
						if (i != 0) {
							if (neighbors[i] != 0) {
								++nonzeroNum;
								if (neighbors[i - 1] == 0)
									++zeroOnePatternNum;
							}
						}
					}
					if (nonzeroNum >= 2 && nonzeroNum <= 6 &&
						zeroOnePatternNum == 1 &&
						(neighbors[5] == 0 || neighbors[7] == 0 ||
						(neighbors[1] == 0 && neighbors[3] == 0))) {
							changed = true;
							dstLine[u] = 0;
					}
				}
			}
		}
		SWAP (srcImage, dstImage);
	}
	for (int v = 0; v < outImage->height; ++v) {
		uchar* imageLine = IMAGE_LINE (outImage, v);
		for (int u = 0; u < outImage->width; ++u) {
			if (imageLine[u] == 255) {
				for (int i = 1; i < 8; i += 2) {
					neighbors[i] = imageLine[u + neighborX[i] +
						neighborY[i] * outImage->widthStep];
				}
				if ((neighbors[1] && neighbors[3]) ||
						(neighbors[3] && neighbors[5]) ||
						(neighbors[5] && neighbors[7]) ||
						(neighbors[7] && neighbors[1])) {
					imageLine[u] = 0;
				}
			}
		}
	}
}